%{

#include "main.h"
#include "node.h"
#include "record.h"
#include "table.h"
#include "tablemanager.h"
#include "dbms.h"
#include <cassert>
#include <stdio.h>
#include <sstream>
#include <iostream>
#include <typeinfo>
#include <string.h>
#include <vector>
#include <map>


extern "C" {
    int numStmt = 0;
    #define YYDEBUG 0
    int yydebug = 0;

    DBMS dbms;
    void yyerror(const char *s);
    extern int yylex(void);
    extern int yylineno;

    map<string, vector<Record> > getResult(vector<string> table, vector<BoolExpr*> boolExprs);
    void selectOutput(vector<AttrExpr*> $2, vector<string> table, vector<BoolExpr*> boolExprs);
    bool isForeignKey(string str);
    bool exist(string value, string attrName, vector<Record> records);
    bool existInMap(string value, map<string, bool> mapKey);

    //Aggregate Function
    void aggregateOutput(AggregateFunction* aggregateFunc, vector<Record> records);
    void aggreGroup(string attr, vector<AggregateFunction*> aggregateList);
    double getSumOrAvg(vector<Record> records, string attrName, string type);
    double getMaxOrMin(vector<Record> records, string attrName, string type);

    vector<Record> getWhereRecords(string tableName, vector<BoolExpr*> boolExprs);
    void search(int n, vector<string> table, vector<BoolExpr*> boolExprs, vector<AttrExpr*> attrExprList, bool output);
    map<string, vector<Record> > tableRecord;
    map<string, vector<Record> > columnRecord;
    map<string, int> vec;
    vector< map<string, int> > vecList;

    bool isDouble(const string& s);
    bool isInteger(const string&);
    bool deleted = false;
    bool visited = false;

}
%}

%token CREATE DROP DATABASE TABLE DESC USE SHOW TABLES
%token PRIMARY FOREIGN KEY DEFAULT CHECK TOKEN_NULL IS
%token INSERT INTO VALUES DELETE UPDATE SET SELECT FROM WHERE FULL
%token JOIN INNER OUTER LEFT RIGHT NATURAL CROSS UNION LIKE
%token AS INTNUM DOUBLE CHAR VARCHAR DECIMAL TEXT USING CONSTRAINT
%token COUNT SUM AVG MIN MAX INTERSECT EXCEPT DISTINCT ON IN GROUP BY
%token AND OR LESS_EQUAL   GREATER_EQUAL  EQUAL   NOT_EQUAL
%token '+'  '-'  '*'  '/'  '='  '>'  '<' '%' '[' ']' '_'

%token <stringValue> IDENTIFIER
%token <intValue> INTEGER
%token <stringValue> STRING_LITERAL
%token <doubleValue> DOUBLE_LITERAL

%type <boolValue> OptConstraints;
%type <intValue> CompareOp;
%type <doubleValue> Constant Expr;
%type <stringValue> BoolOp TableName AttrName ColumnName SetExpr
                    FunctionName GroupByStmt FuzzyMatchItem;
%type <attrValue> AttrValue;
%type <attrValueList> AttrValueList;
%type <literal_Value> LitealValue;
%type <tableStmt> TableStmt;
%type <NameList> AttrNameList TableList ;
%type <insertStmt> InsertStmt;

%type <primaryKey> KeyDec OptKeyDecList;
%type <columnType> ColumnType;
%type <columnDec> ColumnDec;
%type <columnDecList> ColumnDecList;

%type <aggregateFunc> AggregateFunction;
/*%type <aggregateList> AggregateList; */

%type <attrExprList> AttrExprList AttrExprlist;
%type <attrExpr> AttrExpr;
%type <whereClause> WhereClauses QueryWhere;
%type <boolExpr> BoolExpr;
%type <valueExpr> ValueExpr;
%type <setStmt> SetStmt;
%type <setStmtList> SetStmtList;

%left OR
%left AND
%left NOT
%nonassoc EQUAL NOT_EQUAL
%nonassoc LESS_EQUAL GREATER_EQUAL '<' '>'
%left '+' '-'
%left '*' '/'
%nonassoc UMINUS '!'
%nonassoc PPLUS DDEC
%nonassoc '[' '.'
%nonassoc ')' EMPTY

%start SQL_Queries

%%

SQL_Queries
        : SQL_Query
        | SQL_Queries SQL_Query 
        ;

SQL_Query
        : SQL_Line ';' { /* printf("Parsed %d valid SQL statement\n", numStmt++) */ }
        ;

SQL_Line
        : CreatStmt
        | SelectStmt
        | DropStmt
        | ShowStmt
        | UseStmt
        | DescStmt
        | InsertStmt
        | DeleteStmt
        | UpdateStmt
        ;

CreatStmt
        : CREATE DATABASE IDENTIFIER { dbms.createDatabase($3 + ".db"); }
        | CREATE TABLE TableName '(' ColumnDecList OptKeyDecList ')' { dbms.createTable($3, $5, $6); }
        ;

ColumnDecList
        : ColumnDec { $$.push_back($1); }
        | ColumnDecList ',' ColumnDec { $$.push_back($3); }
        ;

ColumnDec
        : ColumnName ColumnType OptConstraints { $$ = new ColumnDec($1, $2, $3); }
        ;

ColumnType
        : INTNUM { $$ = new ColumnType("int"); }
        | VARCHAR { $$ = new ColumnType("varchar"); }
        | DECIMAL { $$ = new ColumnType("decimal"); }
        | CHAR { $$ = new ColumnType("char"); }
        | ColumnType '(' INTEGER ')'
        {
            if($3 <= 0) {
                fprintf(stderr, "Error: size must be greater than 0 (line %d).\n ", yylineno);
                exit(1);
            }
            $$ = new ColumnType($3, $1->type);
        }
        ;

OptKeyDecList
        : ',' KeyDec { $$ = $2; }
        | /* empty */ { $$ = NULL; }
        ;

KeyDec
        : PRIMARY KEY '(' ColumnName ')' { $$ = new PrimaryKey($4); }
        ;

ColumnName
        : IDENTIFIER { $$ = $1; }
        ;

OptConstraints
        : NOT TOKEN_NULL { $$ = true; }
        | /* empty */ { $$ = false; }
        ;

SelectStmt
        : SELECT AttrExprList FROM TableList QueryWhere GroupByStmt
        {
            vector<string> table = $4;
            vector<BoolExpr*> boolExprs;
            boolExprs.clear();
            boolExprs = $5;

            int n = table.size();
            visited = false;
            bool hasAggregate = false;
            vector<AttrExpr*> attrExprList;
            vector<AggregateFunction*> aggregateList;

            for(int i = 0; i < $2.size(); i++) {
                if($2[i]->aggregateFunc != NULL) {
                    hasAggregate = true;
                    aggregateList.push_back($2[i]->aggregateFunc);
                } else {
                    attrExprList.push_back($2[i]);
                }
            }

             if($2.empty()) { // ***Select * FROM****
                for(int i = 0; i < table.size(); i++) {
                    vector<string> attrNameList = dbms.getSegments(table[i]);
                    for(int j = 0; j < attrNameList.size(); j++) {
                        AttrExpr* attrExpr = new AttrExpr(table[i], attrNameList[j]);
                        attrExprList.push_back(attrExpr);
                    }
                }
            }
            for(int i = 0; i < table.size(); i++) {
                tableRecord[table[i]] = dbms.getRecords(table[i]);
            }
            for(int i = 0; i < attrExprList.size(); i++) {
                string tableName = attrExprList[i]->tableName;
                if(tableName == "") {
                    for(int j = 0; j < table.size(); j++) {
                        cout << table[j] + "." + attrExprList[i]->toString() << "     ";
                    }
                } else {
                    cout << attrExprList[i]->toString() << "     ";
                }
            }
            cout << endl;
            bool output = false;
            if(n > 1)
                output = true;
            search(n-1, table, boolExprs, attrExprList, output);

            if(!hasAggregate && n == 1) {
                cout << endl;
                for(int r = 0; r < vecList.size(); r++) {
                    for(int i = 0; i < attrExprList.size(); i++) {
                        string tableName = attrExprList[i]->tableName;
                        string attrName = attrExprList[i]->attrName;
                        if(tableName == "") {
                            for(int j = 0; j < table.size(); j++) {
                                Record record = tableRecord[table[j]][vecList[r][table[j]]];
                                cout << record.find(attrName);
                                if(j != table.size()-1)
                                    cout << " | ";
                            }
                        } else {
                            Record record = tableRecord[tableName][vecList[r][tableName]];
                            cout << record.find(attrName);
                        }
                        if(i != attrExprList.size()-1)
                            cout << " | ";
                        else
                            cout << endl;
                    }
                }
                cout << endl;
            } else if(hasAggregate){
                if(n != 1) {
                    cout << "Error in line: " + yylineno;
                    cout << "Sorry! We do not support aggregate function and group by statement for above two tables";
                    cout << endl;
                    exit(-1);
                }
                if($6 == "") {
                    if(attrExprList.size() > 0) {
                        cout << "Error in line: " + yylineno;
                        cout << "The group by statement must not be null!" << endl;
                        exit(-1);
                    }
                    vector<Record> records;
                    records = getWhereRecords(table[0], $5);
                    for(int i = 0; i < aggregateList.size(); i++) {
                        aggregateOutput(aggregateList[i], records);
                    }
                } else {
                    if(attrExprList.size() != 1) {
                        cout << "Error in line: " + yylineno;
                        cout << "Only support for just one column Select when refer to group by" << endl;
                        exit(-1);
                    }
                    if(attrExprList[0]->attrName != $6) {
                        cout << "Error in line: " + yylineno;
                        cout << "The selected column should be equal to the column name ater group by" << endl;
                        exit(-1);
                    }
                    vector<Record> records;
                    records = getWhereRecords(table[0], $5);
                    vector<string> attrList;
                    for(int i = 0; i < records.size(); i++) {
                        string attr = records[i].find($6);
                        columnRecord[attr].push_back(records[i]);
                        attrList.push_back(attr);
                    }
                    sort(attrList.begin(), attrList.end());
                    vector<string>::iterator it = unique(attrList.begin(), attrList.end());
                    if(it != attrList.end()) {
                        attrList.erase(it, attrList.end());
                    }
                    for(int i = 0; i < aggregateList.size(); i++) {
                        cout << aggregateList[i]->toString();
                        if(i != aggregateList.size()-1)
                            cout << " | ";
                    }
                    cout << endl;

                    for(it = attrList.begin(); it != attrList.end(); it++) {
                        string attr = *it;
                        aggreGroup(attr, aggregateList);
                    }

                }
            }

            for(int i = 0; i < boolExprs.size(); i++)
                delete boolExprs[i];
            for(int i = 0; i < attrExprList.size(); i++) {
                delete attrExprList[i];
            }
            boolExprs.clear();
            tableRecord.clear();
            columnRecord.clear();
            vec.clear();
            vecList.clear();
        }
/*        | SELECT ColumnList ',' AggregateList FROM TableName QueryWhere GroupByStmt
        {
//            if($2 != $8) {
//                cout << "Error in line: " + yylineno << endl;
//                cout << "ColumnName after GrouBy needs equaling to columnName after SELECT ";
//                exit(-1);
//            }
//            vector<Record> records;
//            if($7.empty()) {
//                records = dbms.getRecords($6);
//            } else {
//                records = getWhereRecords($6, $7);
//            }
//            vector<string> attrList;
//            for(int i = 0; i < records.size(); i++) {
//                string attr = records[i].find($8);
//                columnRecord[attr].push_back(records[i]);
//                attrList.push_back(attr);
//            }
//            cout << $2 << "  | ";
//            sort(attrList.begin(), attrList.end());
//            vector<string>::iterator it = unique(attrList.begin(), attrList.end());
//            if(it != attrList.end()) {
//                attrList.erase(it, attrList.end());
//            }
//            for(int i = 0; i < $4.size(); i++) {
//                cout << $4[i]->toString();
//                if(i != $4.size()-1)
//                    cout << " | ";
//            }
//            cout << endl;
//
//            for(it = attrList.begin(); it != attrList.end(); it++) {
//                string attr = *it;
//                aggreGroup(attr, $4);
//            }
//            //*****free memory******
//            columnRecord.clear();
//            for(int i = 0; i < $4.size(); i++) {
//                delete $4[i];
//            }
//            for(int i = 0; i < $7.size(); i++) {
//                delete $7[i];
//            }
        }
        | SELECT AggregateList FROM TableName QueryWhere
        {
            vector<Record> records;
            if($5.empty()) {
                records = dbms.getRecords($4);
            } else {
                records = getWhereRecords($4, $5);
            }
            for(int i = 0; i < $2.size(); i++) {
                aggregateOutput($2[i], records);
                delete $2[i];
            }
        }
        ;

ColumnList
        : AttrName { $$.push_back($1); }
        | ColumnList ',' AttrName { $$.push_back($3); }
        ;

AggregateList
        : AggregateFunction { $$.push_back($1); }
        | AggregateList ',' AggregateFunction { $$.push_back($3); }
        ;*/

AggregateFunction
        : FunctionName '(' AttrName ')' { $$ = new AggregateFunction($1, $3); }
        ;

FunctionName
        : SUM { $$ = "SUM"; }
        | AVG { $$ = "AVG"; }
        | MAX { $$ = "MAX"; }
        | MIN { $$ = "MIN"; }
        | COUNT { $$ = "COUNT"; }
        ;

QueryWhere
        : WHERE WhereClauses { $$ = $2; }
        | /* EMPTY */ { $$.clear(); }
        ;

GroupByStmt
        : GROUP BY AttrName { $$ = $3; }
        | /* EMPTY */ { $$ = ""; }
        ;

AttrExprList
        : AttrExprlist { $$ = $1; }
        | '*' { }
        ;

AttrExprlist
        : AttrExpr { $$.push_back($1); }
        | AttrExprlist ',' AttrExpr { $$.push_back($3); }
        ;

AttrExpr
        : TableName '.' AttrName { $$ = new AttrExpr($1, $3); }
        | AttrName { $$ = new AttrExpr($1); }
        | AggregateFunction { $$ = new AttrExpr($1); }
        ;

TableName
        : IDENTIFIER { $$ = $1; }
        ;

AttrName
        : IDENTIFIER { $$ = $1; }
        ;

TableList
        : TableName { $$.push_back($1); }
        | TableList ',' TableName { $$.push_back($3); }
        ;

WhereClauses
        : BoolExpr { $$.push_back($1); }
        | WhereClauses BoolOp BoolExpr { $$.push_back($3); }
        | '(' WhereClauses ')' { $$ = $2; }
        ;

BoolExpr
        : AttrExpr CompareOp ValueExpr { $$ = new BoolExpr($1, $2, $3); }
        | AttrExpr IS TOKEN_NULL { $$ = new BoolExpr($1, "null"); }
        | AttrExpr IS NOT TOKEN_NULL { $$ = new BoolExpr($1, "notNull"); }
        | AttrExpr LIKE FuzzyMatchItem { $$ = new BoolExpr($1, "LIKE", $3); }
        ;

FuzzyMatchItem
        : STRING_LITERAL { $$ = $1; }
        ;

ValueExpr
        : INTEGER { $$ = new ValueExpr($1, "int"); }
        | STRING_LITERAL { $$ = new ValueExpr($1, "string"); }
        | DOUBLE_LITERAL { $$ = new ValueExpr($1, "double"); }
        | AttrExpr { $$ = new ValueExpr($1, "attrExpr"); }
        ;

Constant
        : INTEGER { double value = (double) $1; $$ = value; }
        | DOUBLE_LITERAL { $$ = $1; }
        ;

CompareOp
        : '=' { $$ = '='; }
        | '>' { $$ = '>'; }
        | '<' { $$ = '<'; }
        | LESS_EQUAL { $$ = '<'+'='; }
        | GREATER_EQUAL { $$ = '>'+'='; }
        | NOT_EQUAL { $$ = '!'+'='; }
        ;

BoolOp
        : AND { $$ = "AND"; }
        ;

DropStmt
        : DROP TABLE TableName { dbms.dropTable($3); }
        | DROP DATABASE IDENTIFIER { dbms.dropDatabase($3 + ".db"); }
        ;

ShowStmt
        : SHOW TABLES { dbms.showTables(); }
        ;

UseStmt
        : USE IDENTIFIER { dbms.useDatabase($2 + ".db"); }
        ;

DescStmt
        : DESC IDENTIFIER { dbms.descTable($2); }
        ;

InsertStmt
        : INSERT INTO TableStmt VALUES AttrValueList
        {
            $$ = new InsertStmt($3, $5);
            vector<string> columnName = dbms.getSegments($3->tableName);
            vector<int> foreignKey;
            for(int i = 0; i < columnName.size(); i++) {
                if(isForeignKey(columnName[i])) {
                    foreignKey.push_back(i);
                }
            }
            vector<string> table, column;
            for(int i = 0; i < foreignKey.size(); i++) {
                vector<string> def = Str::split(columnName[foreignKey[i]], "_");
                if(def.size() == 2) {
                    table.push_back(def[0]);
                    column.push_back(def[1]);
                } else {
                    cout << "Error in line:" + yylineno;
                    cout << "One '_' symbol allowed Only" << endl;
                    exit(-1);
                }
            }
//            vector<vector<Record> > foreignRecords;
//            for(int i = 0; i < table.size(); i++) {
//                foreignRecords.push_back(dbms.getRecords(table[i]));
//            }

            vector<map<string, bool> > mapKeys;
            for(int i = 0; i < table.size(); i++) {
                vector<Record> foreignRecords = dbms.getRecords(table[i]);
                vector<map<string, bool> > currentRecord;
                map<string, bool> currentMap;
                for(int j = 0; j < foreignRecords.size(); j++) {
                    Record record = foreignRecords[j];
                    currentMap[record.find(column[i])] = true;
                }
                mapKeys.push_back(currentMap);
                currentMap.clear();
                foreignRecords.clear();
            }

            assert(table.size() == column.size());
            assert(table.size() == mapKeys.size());

            if($3->all) {
                for(int i = 0; i < $$->segments.size(); i++) {
                    vector<string> segmant = $$->segments[i];
                    for(int j = 0; j < table.size(); j++) {
                        if(existInMap(segmant[foreignKey[j]], mapKeys[j])) {
                        } else {
                            cout << "Error in line: " + yylineno;
                            cout << "No such a value:" + segmant[foreignKey[j]] +  " in Table:" + table[j] +" for column:" + column[j] << endl;
                            exit(-1);
                        }
                    }
                    dbms.insertRecord($$->table->tableName, segmant);
                }
            } else {
            }
            mapKeys.clear();
            table.clear();
            column.clear();
            foreignKey.clear();
            delete $$;
        }
        ;

TableStmt
        : '[' TableStmt ']' { $$ = $2; }
        | TableName { $$ = new TableStmt($1, true); }
        | TableName  AttrNameList { $$ = new TableStmt($1, $2, false); }
        ;

AttrNameList
        : '(' AttrNameList ')' { $$ = $2; }
        | AttrName { $$.push_back($1); }
        | AttrNameList ',' AttrName { $$.push_back($3); }
        ;

AttrValueList
        : AttrValueList ',' '(' AttrValue ')' { $$.push_back($4); }
        | '(' AttrValue ')' { $$.push_back($2); }
        ;

AttrValue
        : LitealValue { $$.push_back($1); }
        | AttrValue ',' LitealValue { $$.push_back($3); }
        ;

LitealValue
        : INTEGER
        {
            ostringstream stm;
            stm << $1;
            $$ = new LiteralValue($1, stm.str(), "int");
        }
        | DOUBLE_LITERAL { $$ = new LiteralValue($1, "double"); }
        | STRING_LITERAL { $$ = new LiteralValue(0, $1, "string"); }
        | TOKEN_NULL { $$ = new LiteralValue(0, "null", "null"); }
        ;

DeleteStmt
        : DELETE FROM TableName WHERE WhereClauses
        {
            string tableName = $3;
            vector<Record> records = getWhereRecords(tableName, $5);
            if(records.empty()) {
                cout << "Nothing find in this WhereExpr " << endl;
                cout << "So, nothing will be deleted" << endl;
                cout << endl;
            }
            for(int i = 0; i < records.size(); i++) {
                dbms.deleteRecord(tableName, records[i].getPrimaryValue());
            }
        }
        ;

UpdateStmt
        : UPDATE TableName SET SetStmtList WHERE WhereClauses
        {
            vector<Record> records = getWhereRecords($2, $6);
            vector<SetStmt*> setStmtList = $4;
            for(int i = 0; i < records.size(); i++) {
                for(int j = 0; j < setStmtList.size(); j++) {
                    if(!isForeignKey(setStmtList[j]->attrName)) {
                        records[i].update(setStmtList[j]->attrName, setStmtList[j]->setExpr);
                        dbms.updateRecord($2, records[i]);
                    } else {
                        vector<string> def = Str::split(setStmtList[j]->attrName, "_");
                        string table = def[0];
                        string column = def[1];
                        vector<Record> recordToFind = dbms.getRecords(table);
                        if(!exist(setStmtList[j]->setExpr, column, recordToFind)) {
                            cout << "Error in line: " << yylineno << " for update statement " << endl;
                            cout << "No such a value:" << setStmtList[j]->setExpr << " in Table:" << table << " for Column:" << column;
                            exit(-1);
                        } else {
                            records[i].update(setStmtList[j]->attrName, setStmtList[j]->setExpr);
                            dbms.updateRecord($2, records[i]);
                        }
                    }
                }
            }
            for(int i = 0; i < setStmtList.size(); i++) {
                delete setStmtList[i];
            }
        }
        ;

SetStmtList
        : SetStmt { $$.push_back($1); }
        | SetStmtList ',' SetStmt { $$.push_back($3); }
        ;

SetStmt
        : AttrName '=' SetExpr { $$ = new SetStmt($1, $3); }
        ;

SetExpr
        : Expr {
            ostringstream stm;
            stm << $1;
            $$ = stm.str();
        }
        | STRING_LITERAL { $$ = $1; }
        | TOKEN_NULL { $$ = ""; }
        ;

Expr
        : Expr '+' Expr { $$ = $1+$3; }
        | Expr '-' Expr { $$ = $1-$3; }
        | '-' Expr %prec UMINUS { $$ = -$2; }
        | Expr '*' Expr { $$ = $1*$3; }
        | Expr '/' Expr { $$ = $1/$3; }
        | Constant { $$ = $1; }
        ;

%%

void yyerror(const char *s) {
    fprintf(stderr, "%s (line %d)\n", s, yylineno);
}
bool isInteger(const std::string & s)
{
   if(s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false ;

   char* p ;
   strtol(s.c_str(), &p, 10) ;

   return (*p == 0) ;
}

map<string, vector<Record> > getResult(vector<string> table, vector<BoolExpr*> boolExprs) {
    vector<BoolExpr*> attrToNotAttr;
    vector<BoolExpr*> attrToAttr;
    map<string, vector<Record> > FinalResult;

    if(boolExprs.empty()) {
        for(int i = 0; i < table.size(); i++) {
            FinalResult[table[i]] = dbms.getRecords(table[i]);
        }
        return FinalResult;
    }

    for(int i = 0; i < boolExprs.size(); i++) {
        AttrExpr* attrExpr = boolExprs[i]->attrExpr;
        ValueExpr* valueExpr = boolExprs[i]->valueExpr;

        if(attrExpr->tableName == "") {
            if(valueExpr->type == "attrExpr") {
                cout << "Error in" + yylineno << " ";
                cout << "In expression " + boolExprs[i]->toString() << endl;
                cout << "Please specify TableName of attributes assignment";
                exit(1);
            }
            attrToNotAttr.push_back(boolExprs[i]);
        } else {
            if(valueExpr->type == "attrExpr") {
                if(valueExpr->attrExpr->tableName == "") {
                    cout << "Error in" + yylineno << " ";
                    cout << "In expression " + boolExprs[i]->toString() << endl;
                    cout << "Please specify TableName of attributes assignment";
                    exit(1);
                }
                attrToAttr.push_back(boolExprs[i]);
            } else {
                attrToNotAttr.push_back(boolExprs[i]);
            }
        }
    }

    map<string, vector<Record> > Fresult;
    map<string, vector<Record> > Sresult;
    vector<int> visited;
    map<string, int> tableSeq;
    for(int i = 0; i < table.size(); i++) {
        tableSeq[table[i]] = i;
        vector<Record> records = dbms.getRecords(table[i]);
        vector<Record> resRecord;
        //cout << records.size() << " ";
        if(records.empty()) {
            visited.push_back(1);
            continue;
        }
        bool added = false;
        for(int j = 0; j < records.size(); j++) {
            bool boolAns = true;
            //records[j].output();
            for(int k = 0; k < attrToNotAttr.size(); k++) {
                AttrExpr* attrExpr = attrToNotAttr[k]->attrExpr;
                ValueExpr* valueExpr = attrToNotAttr[k]->valueExpr;

                if(attrExpr->tableName == "" || attrExpr->tableName == table[i]) {
                    boolAns = boolAns && attrToNotAttr[k]->res(records[j].find(attrExpr->attrName));
                    if(!added) {
                        added = true;
                        visited.push_back(1);
                    }
                }
            }
            if(!added)
            {
                visited.push_back(0);
                break;
            }
            if(boolAns) {
                resRecord.push_back(records[j]);
                Fresult[table[i]].push_back(records[j]);
            }
        }
    }

    for(int i = 0; i < table.size(); i++) {

        vector<Record> records;
        if(visited[i] == 0) {
            records = dbms.getRecords(table[i]);
        } else {
            map<string, vector<Record> >::iterator it;
            it = Fresult.find(table[i]);
            if(it != Fresult.end()) {
                records = it->second;
            } else {
                continue;
            }
        }

        for(int j = 0; j < attrToAttr.size(); j++) {
            AttrExpr* attrExpr = attrToAttr[j]->attrExpr;
            ValueExpr* valueExpr = attrToAttr[j]->valueExpr;
            AttrExpr* rightAttrExpr = valueExpr->attrExpr;

            if(attrExpr->attrName != table[i]) {
                continue;
            } else {
                vector<Record> rightTableRes;
                map<string, vector<Record> >::iterator it1;
                it1 = Fresult.find(rightAttrExpr->tableName);
                if(it1 != Fresult.end()) {
                    rightTableRes = it1->second;
                }
                map<string, int>::iterator it2;
                it2 = tableSeq.find(rightAttrExpr->tableName);
                int seq;
                if(it2 != tableSeq.end())
                    seq = it2->second;
                else {
                    cout << "Error in line " + yylineno  << endl;
                    cout << "Please add the table name " + rightAttrExpr->tableName + " to the FROM List" << endl;
                    exit(1);
                }
                if(rightTableRes.empty() && visited[seq])
                    continue;
                else if(rightTableRes.empty() && !visited[seq]) {
                    rightTableRes = dbms.getRecords(rightAttrExpr->tableName);
                }
                for(int k = 0; k < rightTableRes.size(); k++) {
                    for(int r = 0; r < records.size(); r++) {
                        string leftString = records[r].find(attrExpr->attrName);
                        string rightString = rightTableRes[k].find(rightAttrExpr->attrName);
                        if(isInteger(leftString) && isInteger(rightString)) {
                            if(attrToAttr[j]->res(leftString, rightString, 0)) {
                                Sresult[table[i]].push_back(records[r]);
                            }
                        } else if(!isInteger(leftString) && !isInteger(rightString)) {
                            if(attrToAttr[j]->res(leftString, rightString, 1))
                                Sresult[table[i]].push_back(records[r]);
                        } else {
                            cout << "Error in line " + yylineno  << endl;
                            cout << "String value should not compare to int value" << endl;
                            exit(1);
                        }
                    }
                }

            }
        }
    }

    for(int i = 0; i < table.size(); i++) {
        map<string, vector<Record> >::iterator itS;
        itS = Sresult.find(table[i]);
        if(visited[i] == 0) {
            if(itS != Sresult.end()) {
                FinalResult[table[i]] = itS->second;
            }
        } else {
            map<string, vector<Record> >::iterator itF;
            itF = Fresult.find(table[i]);
            if(itF != Fresult.end()) {
                if(itS != Sresult.end()) {
                    FinalResult[table[i]] = itS->second;
                } else {
                    FinalResult[table[i]] = itF->second;
                }
            } else {
                continue;
            }
        }
    }
    return FinalResult;
}

void sOutput(map<string, vector<Record> > FinalResult, vector<string> key, string tableName) {
    for(int j = 0; j < FinalResult[tableName].size(); j++) {
        for(int k = 0; k < key.size(); k++) {
            cout << FinalResult[tableName][j].find(key[k]);
            if(k != key.size()-1)
                cout << " | ";
        }
        cout << endl;
    }
    cout << endl;
}

void selectOutput(vector<AttrExpr*> attrExprList, vector<string> table, vector<BoolExpr*> boolExprs) {
    map<string, vector<Record> > FinalResult = getResult(table, boolExprs);
    for(int i = 0; i < table.size(); i++) {
        cout << "Table " << table[i] << endl;
        if(attrExprList.empty()) {
            vector<string> key = dbms.getSegments(table[i]);
            if(i == 0) {
                for(int j = 0; j < key.size(); j++) {
                    cout << key[j] << "     ";
                }
                cout << endl;
            }
            sOutput(FinalResult, key, table[i]);
        } else {
            vector<string> attrList;
            for(int j = 0; j < attrExprList.size(); j++) {
                string tableName = attrExprList[j]->tableName;
                string attrName = attrExprList[j]->attrName;
                if(tableName == "" || tableName == table[i]) {
                    if(i == 0) {
                        cout << attrName << "     ";
                    }
                    attrList.push_back(attrName);
                }
                if(j == attrExprList.size()-1)
                    cout << endl;
            }
            sOutput(FinalResult, attrList, table[i]);
        }
    }
}

void search(int n, vector<string> table, vector<BoolExpr*> boolExprs, vector<AttrExpr*> attrExprList, bool output) {
    if(n == -1) {
        bool boolAns = true;
        for(int i = 0; i < boolExprs.size(); i++) {
            AttrExpr* attrExpr = boolExprs[i]->attrExpr;
            ValueExpr* valueExpr = boolExprs[i]->valueExpr;

            if(attrExpr->tableName == "") {
                if(valueExpr != NULL) {
                    if(valueExpr->type == "attrExpr") {
                        cout << "Error in line " + yylineno << endl;
                        cout << "Please assign the table Name of bool expression: " + boolExprs[i]->toString();
                        exit(-1);
                    } else {
                        for(int j = 0; j < table.size(); j++) {
//                            Record record = dbms.getRecords(table[j])[vec[table[j]]];
                            Record record = tableRecord[table[j]][vec[table[j]]];
                            boolAns = boolAns && boolExprs[i]->res(record.find(attrExpr->attrName));
                            if(!boolAns) {
                                return;
                            }
                        }
                    }
                } else {
                    for(int j = 0; j < table.size(); j++) {
                        Record record = tableRecord[table[j]][vec[table[j]]];
                        boolAns = boolAns && boolExprs[i]->res(record.find(attrExpr->attrName));
                        if(!boolAns)
                            return;
                    }
                }
            } else {
                if(valueExpr->type != "attrExpr") {
                    string LtableName = attrExpr->tableName;
                    string LattrName = attrExpr->attrName;
                    Record record = tableRecord[LtableName][vec[LtableName]];
                    boolAns = boolAns && boolExprs[i]->res(record.find(LattrName));
                } else {
                    string LtableName = attrExpr->tableName;
                    string RtableName = valueExpr->attrExpr->tableName;
                    string LattrName = attrExpr->attrName;
                    string RattrName = valueExpr->attrExpr->attrName;
                    if(RtableName == "") {
                        cout << "Error in line " + yylineno << endl;
                        cout << "Please assign the table Name of bool expression: " + boolExprs[i]->toString();
                        exit(-1);
                    }
//                    Record Lrecord = dbms.getRecords(LtableName)[vec[LtableName]];
                    Record Lrecord = tableRecord[LtableName][vec[LtableName]];
                    Record Rrecord = tableRecord[RtableName][vec[RtableName]];
                    string leftValue = Lrecord.find(LattrName);
                    string rightValue = Rrecord.find(RattrName);
                    if(isDouble(rightValue) && isDouble(leftValue)) {
                        boolAns = boolAns && boolExprs[i]->res(leftValue, rightValue, 0);
                    } else {
                        boolAns = boolAns && boolExprs[i]->res(leftValue, rightValue, 1);
                    }
                }
                if(!boolAns)
                    return;
            }
        }

/*        if(!visited) {
            for(int i = 0; i < attrExprList.size(); i++) {
                string tableName = attrExprList[i]->tableName;
                if(tableName == "") {
                    for(int j = 0; j < table.size(); j++) {
                        cout << table[j] + "." + attrExprList[i]->toString() << "     ";
                    }
                } else {
                    cout << attrExprList[i]->toString() << "     ";
                }
            }
            visited = true;
        }

        cout << endl;*/

        //******Output*******
        if(output) {
            for(int i = 0; i < attrExprList.size(); i++) {
                string tableName = attrExprList[i]->tableName;
                string attrName = attrExprList[i]->attrName;
                if(tableName == "") {
                    for(int j = 0; j < table.size(); j++) {
                        Record record = tableRecord[table[j]][vec[table[j]]];
                        cout << record.find(attrName);
                        if(j != table.size()-1)
                            cout << " | ";
                    }
                } else {
                    Record record = tableRecord[tableName][vec[tableName]];
                    cout << record.find(attrName);
                }
                if(i != attrExprList.size()-1)
                    cout << " | ";
                else
                    cout << endl;
            }
        }
        vecList.push_back(vec);
        return;
    }
    for(int j = 0; j < tableRecord[table[n]].size(); j++) {
        vec[table[n]] = j;
        search(n-1, table, boolExprs, attrExprList, output);
    }
}

vector<Record> getWhereRecords(string tableName, vector<BoolExpr*> boolExprs) {
    vector<Record> records = dbms.getRecords(tableName);
    vector<Record> result;
    for(int i = 0; i < records.size(); i++) {
        bool boolAns = true;
        for(int j = 0; j < boolExprs.size(); j++) {
            string attrName = boolExprs[j]->attrExpr->attrName;
            boolAns = boolAns && boolExprs[j]->res(records[i].find(attrName));
            if(!boolAns)
                break;
        }
        if(boolAns) {
            result.push_back(records[i]);
        }
    }
    for(int i = 0; i < boolExprs.size(); i++) {
        delete boolExprs[i];
    }
    return result;
}

bool isDouble(const string& s) {
    istringstream iss(s);
    double x;
    char c;
    return iss >> x && !(iss >> c);
}

bool isNull(const string& s) {
    return s == "null" || s == "NULL";
}

bool isForeignKey(string str) {
    int len = str.size();
    int _count = 0;
    for(int i = 0; i < len; i++) {
        if(str[i] == '_')
            _count ++;
    }
    if(_count == 1)
        return true;
    return false;
}

bool exist(string value, string attrName, vector<Record> records) {
    for(int i = 0; i < records.size(); i++) {
        if(records[i].find(attrName) == value) {
            return true;
        }
    }
    return false;
}

bool existInMap(string value, map<string, bool> mapKey) {
    map<string, bool>::iterator it;
    it = mapKey.find(value);
    if(it != mapKey.end())
        return true;
    return false;
}

void aggreGroup(string attr, vector<AggregateFunction*> aggregateList) {
    vector<Record> records = columnRecord[attr];
    cout << attr << "   | ";
    for(int i = 0; i < aggregateList.size(); i++) {
        string type = aggregateList[i]->aggreType;
        if(type == "SUM") {
            cout << getSumOrAvg(records, aggregateList[i]->attrName, "SUM");
        } else if(type == "AVG") {
            cout << getSumOrAvg(records, aggregateList[i]->attrName, "AVG");
        } else if(type == "MAX") {
            cout << getMaxOrMin(records, aggregateList[i]->attrName, "MAX");
        } else if(type == "MIN") {
            cout << getMaxOrMin(records, aggregateList[i]->attrName, "MIN");
        } else if(type == "COUNT") {
            cout << getSumOrAvg(records, aggregateList[i]->attrName, "COUNT");
        }
        if(i != aggregateList.size()-1)
            cout << "      | ";
    }
    cout << endl;
}

void aggregateOutput(AggregateFunction* aggregateFunc, vector<Record> records) {
    string type = aggregateFunc->aggreType;
    int kind = -1;
    if(type == "SUM") {
        kind = 0;
    } else if(type == "AVG") {
        kind = 1;
    } else if(type == "MAX") {
        kind = 2;
    } else if(type == "MIN") {
        kind = 3;
    } else if(type == "COUNT") {
        kind = 4;
    }
    switch(kind) {
        case 0:
            cout << "SUM(" << aggregateFunc->attrName << "):" << getSumOrAvg(records, aggregateFunc->attrName, "SUM");
            cout << endl;
            break;

        case 1:
            cout << "AVG(" << aggregateFunc->attrName << "):" << getSumOrAvg(records, aggregateFunc->attrName, "AVG");
            cout << endl;
            break;

        case 2:
            cout << "MAX(" << aggregateFunc->attrName << "):" << getMaxOrMin(records, aggregateFunc->attrName, "MAX");
            cout << endl;
            break;

        case 3:
            cout << "MIN(" << aggregateFunc->attrName << "):" << getMaxOrMin(records, aggregateFunc->attrName, "MIN");
            cout << endl;
            break;

        case 4:
            cout << "COUNT(" << aggregateFunc->attrName << "):" << getSumOrAvg(records, aggregateFunc->attrName, "COUNT");
            cout << endl;
            break;

        default:
            break;
    }
}

double getSumOrAvg(vector<Record> records, string attrName, string type) {
    double sum = 0;
    double num = 0;
    for(int i = 0; i < records.size(); i++) {
        string res = records[i].find(attrName);
        if(!isDouble(res) && !isNull(res)) {
            cout << "Error in line " << yylineno << endl;
            cout << "The value in column " << attrName << " is not int or double value!" << endl;
            exit(-1);
        }
        if(!isNull(res)) {
            sum += stod(res);
            num += 1;
        }
    }
    if(type == "SUM")
        return sum;
    else if(type == "AVG"){
        if(num == 0) {
            cout << "0 records found!";
        } else {
            return sum/num;
        }
    } else if(type == "COUNT") {
        return num;
    }
}

double getMaxOrMin(vector<Record> records, string attrName, string type) {
    double max, min;
    int start = 0;
    for(int i = 0; i < records.size(); i++) {
        string res = records[i].find(attrName);
        if(!isDouble(res) && !isNull(res)) {
            cout << "Error in line " << yylineno << endl;
            cout << "The value in column " << attrName << " is not int or double value!" << endl;
            exit(-1);
        }
        if(isNull(res))
            continue;
        start = i;
        max = min = stod(res);
        break;
    }

    int i = start+1;
    while(i < records.size()) {
        string res = records[i].find(attrName);
        if(!isNull(res)) {
            double a = stod(res);
            double b = a;
            while(++i < records.size()) {
                string res1 = records[i].find(attrName);
                if(!isNull(res1)) {
                    b = stod(res1);
                    break;
                }
            }
            if(b > a)
                swap(a, b);
            max = max >= a ? max : a;
            min = min <= b ? min : b;
        }
        i++;
    }
    if(type == "MAX") {
        return max;
    } else if(type == "MIN") {
        return min;
    }
}

int main(int argc, char** argv) {
    int i;
    for (i=1; i<argc; ++i) {
        FILE *fp = fopen(argv[i], "r");
        if (fp) {
            printf("\n");
            stdin = fp;
            if (!yyparse())
                printf("\nParsed successfully!\n");
            else
                printf("Please check your code.\n");
            fclose(fp);
        } else {
            char buf[100];
            sprintf(buf, "Error opening file '%s'", argv[i]);
         perror(buf);
        }
        yylineno = 0;
    }
    dbms.closeDatabase();
    fclose(stdout);
    return 0;
}
/*int main() {
    yylineno = 0;
    if(!yyparse())
        printf("\nParsed successfully!\n");
    else
        printf("Please check your code.\n");
    dbms.closeDatabase();
    return 0;
}*/

